﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.Tools.TraceVisualiser.Components;
using System.Windows.Forms;
using System.Drawing;
using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Forms;

namespace Noris.Tools.TraceVisualiser.Support
{
    /// <summary>
    /// Hlavní správce podkladových dat aplikace.
    /// Používá se pro získání dat trvalého charakteru i pro volání systémových metod.
    /// </summary>
    /// <remarks>
    /// Obsahuje privátní singleton na svoji instanci, v níž uchovává trvalá data.
    /// Uchovává v sobě konfiguraci, reflexi výkonných tříd.
    /// </remarks>
    public partial class Steward
    {
        #region Globální funkce
        /// <summary>
        /// Sada globálních funkcí
        /// </summary>
        public static FunctionMenuSet FunctionGlobalSet { get { return Current._FunctionGlobalSet; } }
        /// <summary>
        /// Zajistí načtení a vrácení sady globálních funkcí
        /// </summary>
        private void _FunctionMenuGlobalInit()
        {
            // Globální funkce:
            FunctionMenuSet globalSet = new FunctionMenuSet();
            IEnumerable<IFunctionGlobalProvider> functions = this._GetServiceObjectList<IFunctionGlobalProvider>(true);
            foreach (IFunctionGlobalProvider function in functions)
            {
                if (function == null) continue;

                // Zeptáme se postupně každé funkce, jaké položky menu poskytuje:
                FunctionGlobalDeclareArgs args = new FunctionGlobalDeclareArgs();
                function.CreateMenuItems(args);
                // Zařadíme položky funkcí do menu:
                globalSet.AddRange(function.GetType(), args.MenuItems);
            }
            this._FunctionGlobalSet = globalSet;
        }
        #endregion
        #region Kontextové funkce - načtení menu pro konkrétní situaci
        /// <summary>
        /// Pro daný klíč najde a vrátí definici položek kontextového menu.
        /// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
        /// Definice menu je soupis vhodných nabídek pro danou situaci.
        /// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionContextMenuItem, 
        /// a to pomocí jejich metody GetMenuSuitableFor().
        /// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
        /// </summary>
        /// <param name="graphMode">Typ grafu</param>
        /// <param name="menuKind">Prostor, kde se kliklo</param>
        /// <param name="elementDrawType">Druh elementu</param>
        /// <param name="rowClassNumber">Číslo třídy řádku</param>
        /// <param name="elementClassNumber">Číslo třídy elementu</param>
        /// <returns></returns>
        public static FunctionContextGroupCls FunctionContextGetMenu(RowGraphMode graphMode, FunctionContextMenuItemAreaType menuKind, GraphElementShapeType elementShapeType, int rowClassNumber, int elementClassNumber)
        {
            FunctionContextMenuItemKey menuKey = new FunctionContextMenuItemKey(graphMode, menuKind, elementShapeType, rowClassNumber, elementClassNumber);
            return Current._FunctionContextGetMenu(menuKey);
        }
        /// <summary>
        /// Pro daný klíč najde a vrátí definici položek kontextového menu.
        /// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
        /// Definice menu je soupis vhodných nabídek pro danou situaci.
        /// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionContextMenuItem, 
        /// a to pomocí jejich metody GetMenuSuitableFor().
        /// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
        /// </summary>
        /// <param name="menuKey">Klíč situace, pro kterou se hledá menu</param>
        /// <returns></returns>
        public static FunctionContextGroupCls FunctionContextGetMenu(FunctionContextMenuItemKey menuKey)
        {
            return Current._FunctionContextGetMenu(menuKey);
        }
        #endregion
        #region Kontextové funkce - určení vhodných funkcí pro konkrétní situaci
        /// <summary>
        /// Pro daný klíč najde a vrátí definici položek kontextového menu.
        /// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
        /// Definice menu je soupis vhodných nabídek pro danou situaci.
        /// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionContextMenuItem, 
        /// a to pomocí jejich metody GetMenuSuitableFor().
        /// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
        /// </summary>
        /// <param name="graphMode">Typ grafu</param>
        /// <param name="menuKind">Prostor, kde se kliklo</param>
        /// <param name="elementDrawType">Druh elementu</param>
        /// <param name="rowClassNumber">Číslo třídy řádku</param>
        /// <param name="elementClassNumber">Číslo třídy elementu</param>
        /// <returns></returns>
        private FunctionContextGroupCls _FunctionContextGetMenu(RowGraphMode graphMode, FunctionContextMenuItemAreaType menuKind, GraphElementShapeType elementShapeType, int rowClassNumber, int elementClassNumber)
        {
            FunctionContextMenuItemKey menuKey = new FunctionContextMenuItemKey(graphMode, menuKind, elementShapeType, rowClassNumber, elementClassNumber);
            return this._FunctionContextGetMenu(menuKey);
        }
        /// <summary>
        /// Pro daný klíč najde a vrátí definici položek kontextového menu.
        /// Klíčem je typ grafu, třída řádku a dat a grafický typ elementu.
        /// Definice menu je soupis vhodných nabídek pro danou situaci.
        /// Vhodné nabídky systém vybírá z dostupných poskytovatelů služby typu IFunctionContextMenuItem, 
        /// a to pomocí jejich metody GetMenuSuitableFor().
        /// Při příštím požadavku na získání menu pro identickou situaci je seznam položek již vrácen z paměti.
        /// </summary>
        /// <param name="menuKey">Klíč situace, pro kterou se hledá menu</param>
        /// <returns></returns>
        private FunctionContextGroupCls _FunctionContextGetMenu(FunctionContextMenuItemKey menuKey)
        {
            FunctionContextGroupCls value;
            // 1. Pokud už mám pro daný klíč nalezenou definici menu, rovnou ji vrátím:
            if (this._FunctionContextGroupDict.TryGetValue(menuKey, out value))
                return value;

            // 2. Pro daný klíč nevím, která skupina menu bude ta pravá - zeptám se všech funkcí, zda se chtějí zobrazit:
            lock (this._FunctionLockWrite)
            {
                if (this._FunctionContextGroupDict.TryGetValue(menuKey, out value)) return value;
                this._FunctionContextCreateMenuGroup(menuKey);
                if (this._FunctionContextGroupDict.TryGetValue(menuKey, out value)) return value;
            }
            return null;
        }
        /// <summary>
        /// Ze všech dostupných funkcí vybere ty, které jsou vhodné pro danou situaci, 
        /// a z nich sestavenou definici menu uloží do this._FunctionContextGroupDict
        /// </summary>
        /// <param name="menuKey">Klíč situace, pro kterou se hledá menu</param>
        private void _FunctionContextCreateMenuGroup(FunctionContextMenuItemKey menuKey)
        {
            IEnumerable<IFunctionContextMenuItem> functions = this._GetServiceObjectList<IFunctionContextMenuItem>(true);
            FunctionContextGroupCls menuItemList = new FunctionContextGroupCls(menuKey);
            foreach (IFunctionContextMenuItem function in functions)
            {
                if (function == null) continue;

                // Zeptáme se postupně každé funkce, zda chce být zobrazena v menu pro danou situaci:
                FunctionContextMenuItemSuitableArgs args = new FunctionContextMenuItemSuitableArgs(menuKey);
                bool isSuitable = function.IsFunctionSuitableFor(args);
                if (!isSuitable) continue;

                // Zařadíme funkci do menu:
                menuItemList.AddOneItem(function.GetType(), args);
            }
            // Zařadíme celé menu do paměti pro danou situaci:
            this._FunctionContextGroupDict.Add(menuKey, menuItemList);
        }
        #endregion
        #region Subsystém funkcí - konstrukce
        /// <summary>
        /// Iniciace subsystému funkcí
        /// </summary>
        private void _FunctionMenuInit()
        {
            this._FunctionLockWrite = new object();
            this._FunctionMenuGlobalInit();
            this._FunctionContextGroupDict = new Dictionary<FunctionContextMenuItemKey, FunctionContextGroupCls>();
        }
        /// <summary>
        /// Ukončení života subsystému funkcí
        /// </summary>
        private void _FunctionMenuDispose()
        {
            this._FunctionContextGroupDict.Clear();
            this._FunctionLockWrite = null;
        }
        /// <summary>
        /// Sada globálních funkcí
        /// </summary>
        private FunctionMenuSet _FunctionGlobalSet;
        /// <summary>
        /// Pro každou situaci (situace je definovaná svým klíčem FunctionContextMenuItemKey)
        /// je zde uložen jeden záznam, který obsahuje soupis funkcí, které se mají v dané situaci zobrazovat.
        /// </summary>
        private Dictionary<FunctionContextMenuItemKey, FunctionContextGroupCls> _FunctionContextGroupDict;
        private object _FunctionLockWrite;
        #endregion
    }
    #region class FunctionContextGroupCls : seznam všech položek menu pro jeden konkrétní kontext (situace)
    /// <summary>
    /// FunctionContextGroupCls : seznam všech položek menu pro jeden konkrétní kontext (situace)
    /// (pro jednu kombinaci FunctionContextMenuItemKey).
    /// Seznam funkcí je setříděný a dokáže ze sebe vygenerovat menu.
    /// </summary>
    public class FunctionContextGroupCls : IDisposable
    {
        #region Konstrukce
        internal FunctionContextGroupCls(FunctionContextMenuItemKey menuKey)
        {
            this._MenuKey = menuKey;
            this._MenuSet = new FunctionMenuSet();
        }
        private FunctionContextMenuItemKey _MenuKey;
        private FunctionMenuSet _MenuSet;
        #endregion
        #region Property
        /// <summary>
        /// Klíč situace pro toto menu
        /// </summary>
        internal FunctionContextMenuItemKey MenuKey { get { return this._MenuKey; } }
        /// <summary>
        /// Položky menu = jednotlivé funkce
        /// </summary>
        internal FunctionMenuSet MenuSet { get { return this._MenuSet; } }
        #endregion
        #region Metody = převolání metod FunctionMenuSet
        /// <summary>
        /// Do menu vloží položky menu, které jsou deklarovány jedním providerem.
        /// Poznámka: položky není třeba explicitně třídit, interně si zajišťují třídění OnDemand.
        /// </summary>
        /// <param name="functionType"></param>
        /// <param name="args"></param>
        internal void AddOneItem(Type functionType, FunctionContextMenuItemSuitableArgs args)
        {
            this.MenuSet.AddRange(functionType, args.MenuItems);
        }
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            this.MenuSet.Dispose();
        }
        /// <summary>
        /// Vytvoří ze svých dat a vrátí kontextové menu.
        /// Pokud si z něj uživatel vybere, je třeba odchytit event menu ItemClicked, ošetřit jej (vygenerovat argumenty pro běh)
        /// a předat řízení do metody this.ItemClicked(ToolStripItem toolItem, FunctionContextMenuItemRunArgs args)
        /// </summary>
        /// <returns></returns>
		public ContextMenuStrip CreateContextMenuStrip(FunctionContextMenuItemRunArgs args, EventHandler onClickHandler)
        {
			return this.MenuSet.CreateContextMenuStrip(args, onClickHandler);
        }
        /// <summary>
        /// Někdo kliknul na položku menu
        /// </summary>
        /// <param name="e"></param>
        public void ItemClicked(ToolStripItem toolItem, FunctionContextMenuItemRunArgs args)
        {
            this.MenuSet.ItemClicked(toolItem, args);
        }
        #endregion
    }
    #endregion
    #region class FunctionMenuSet : sada funkcí. Buď pro jeden kontext, anebo globální funkce. Obsahuje položky funkcí FunctionMenuElement. Umožní vygenerovat sadu svých položek do menu nebo do toolbaru.
    /// <summary>
    /// FunctionMenuSet : sada funkcí. Buď pro jeden kontext, anebo globální funkce.
    /// Obsahuje položky funkcí FunctionMenuElement.
    /// Umožní vygenerovat sadu svých položek do menu nebo do toolbaru.
    /// </summary>
    public class FunctionMenuSet : IDisposable
    {
        #region Konstrukce
        internal FunctionMenuSet()
        {
            this._MenuElements = new List<FunctionMenuElement>();
        }
        private List<FunctionMenuElement> _MenuElements;
        /// <summary>
        /// Souhrn položek menu
        /// </summary>
        internal List<FunctionMenuElement> MenuElements { get { return this._MenuElementsSorted(); } private set { this._MenuElements = value; } }
        /// <summary>
        /// Příznak, že seznam je setříděn
        /// </summary>
        internal bool IsSorted { get; private set;}
		#endregion
		#region Přidávání, třídění, Dispose
        /// <summary>
        /// Přidá do sebe definici položky menu včetně jejich providera
        /// </summary>
        /// <param name="functionType"></param>
        /// <param name="menuItem"></param>
        internal void Add(Type functionType, FunctionMenuItem menuItem)
        {
            this._AddMenuItem(functionType, menuItem);
            this.IsSorted = false;
        }
        /// <summary>
        /// Přidá do sebe definice položek menu včetně jejich providera
        /// </summary>
        /// <param name="functionType"></param>
        /// <param name="menuItems"></param>
        internal void AddRange(Type functionType, IEnumerable<FunctionMenuItem> menuItems)
        {
            foreach (FunctionMenuItem menuItem in menuItems)
                this._AddMenuItem(functionType, menuItem);
            this.IsSorted = false;
        }
        /// <summary>
        /// Přidá do sebe jednu položku menu hlavní úrovně
        /// </summary>
        /// <param name="functionType"></param>
        /// <param name="menuItem"></param>
        private void _AddMenuItem(Type functionType, FunctionMenuItem menuItem)
        {
            FunctionMenuElement menuElement = _CreateMenuElement(functionType, menuItem);
            this._MenuElements.Add(menuElement);
        }
        /// <summary>
        /// Vytvoří a vrátí jednu položku menu včetně všech submenu
        /// </summary>
        /// <param name="functionType"></param>
        /// <param name="menuItem"></param>
        private FunctionMenuElement _CreateMenuElement(Type functionType, FunctionMenuItem menuItem)
        {
            FunctionMenuElement menuElement = new FunctionMenuElement(functionType, menuItem);
            if (menuItem.ContainSubItems)
            {
                foreach (FunctionMenuItem subItem in menuItem.SubItems)
                    menuElement.SubElements.Add(this._CreateMenuElement(functionType, subItem));
            }
            return menuElement;
        }
        /// <summary>
        /// Vrací setříděný seznam položek
        /// </summary>
        private List<FunctionMenuElement> _MenuElementsSorted()
        {
            if (!this.IsSorted)
            {
                this._MenuElements.Sort(FunctionMenuElement.CompareByOrder);
                this.IsSorted = true;
            }
            return this._MenuElements;
        }
        public void Dispose()
        {
            this._MenuElements.Clear();
        }
		#endregion
        #region Tvorba položek menu, položek toolbaru
        /// <summary>
        /// Vytvoří ze svých dat a vrátí kontextové menu.
        /// Pokud si z něj uživatel vybere, je třeba odchytit event menu ItemClicked, ošetřit jej (vygenerovat argumenty pro běh)
        /// a předat řízení do metody this.ItemClicked(ToolStripItem toolItem, FunctionContextMenuItemRunArgs args)
        /// </summary>
        /// <returns></returns>
		public ContextMenuStrip CreateContextMenuStrip(FunctionContextMenuItemRunArgs runArgs, EventHandler onClickHandler)
        {
            List<FunctionMenuElement> menuElements = this.MenuElements;
            if (menuElements == null || menuElements.Count == 0) return null;

            // Nejprve si zjistím, jaký stav Enabled bude pro jednotlivé MenuItems, a teprve až potom sestavím menu.
            // Důvod: občasné problémy s pamětí při současném provádění.
            this.DetectContextState(runArgs);

            // Kontextové menu:
            ContextMenuStrip menu = new ContextMenuStrip();
            menu.AutoClose = true;
            menu.DropShadowEnabled = true;
            // menu.LayoutStyle = ToolStripLayoutStyle.HorizontalStackWithOverflow;
            menu.RenderMode = ToolStripRenderMode.Professional;
            menu.ShowCheckMargin = false;
            menu.ShowImageMargin = true;
            menu.ShowItemToolTips = true;
            menu.Text = "Nabídka funkcí";

            // Titulek plus podtržení:
            ToolStripLabel label = new ToolStripLabel("Nabídka funkcí");
            label.AutoSize = true;
            label.DisplayStyle = ToolStripItemDisplayStyle.Text;
            label.Font = new Font(label.Font, FontStyle.Bold);
            menu.Items.Add(label);
            menu.Items.Add(new ToolStripSeparator());

            // Přidat položky:
			menu.Items.AddRange(this.CreateMenuItems(true, onClickHandler));
            return menu;
        }
        /// <summary>
        /// Zajistí pro všechny svoje položky, že bude korektně nastavena jejich hodnota IsEnabledForCurrentContext.
        /// Pokud enabled konkrétní položky závisí na situaci, metoda se zeptá providera. Jinak nastaví true.
        /// </summary>
        /// <param name="runArgs"></param>
        internal void DetectContextState(FunctionContextMenuItemRunArgs runArgs)
        {
            DetectContextState(runArgs, this.MenuElements);
        }
        /// <summary>
        /// Určí stave Enabled/Disabled pro položky menu ze seznamu, pro každou z nich i pro jejich SubItems, pokud je mají.
        /// </summary>
        /// <param name="runArgs"></param>
        /// <param name="menuElements"></param>
        internal void DetectContextState(FunctionContextMenuItemRunArgs runArgs, List<FunctionMenuElement> menuElements)
        {
            foreach (FunctionMenuElement menuElement in menuElements)
            {
                menuElement.SetEnabledForContextState(runArgs);
                if (menuElement.ContainSubElements)
                    // Rekurzivně i podřízené položky:
                    DetectContextState(runArgs, menuElement.SubElements);
            }
        }
        /// <summary>
        /// Vytvoří a vrátí položky menu pro this objekt. Neřeší detekci Enabled.
        /// </summary>
        /// <returns></returns>
		internal ToolStripItem[] CreateMenuItems(bool withImage, EventHandler onClickHandler)
        {
			return CreateMenuItems(false, this.MenuElements, withImage, onClickHandler);
        }
        /// <summary>
        /// Vytvoří a vrátí položky menu pro this objekt. Neřeší detekci Enabled.
        /// </summary>
        /// <returns></returns>
        internal ToolStripItem[] CreateMenuItems(bool prependSeparator, bool withImage, EventHandler onClickHandler)
        {
            return CreateMenuItems(prependSeparator, this.MenuElements, withImage, onClickHandler);
        }
        /// <summary>
        /// Vytvoří a vrátí položky menu pro daná seznam elementů.
        /// Neřeší detekci Enabled.
        /// </summary>
        /// <param name="menuElements"></param>
        /// <param name="withImage"></param>
		/// <param name="onClickHandler"></param>
        /// <returns></returns>
        internal static ToolStripItem[] CreateMenuItems(List<FunctionMenuElement> menuElements, bool withImage, EventHandler onClickHandler)
        {
            return CreateMenuItems(false, menuElements, withImage, onClickHandler);
        }
        /// <summary>
        /// Vytvoří a vrátí položky menu pro daná seznam elementů.
        /// Neřeší detekci Enabled.
        /// </summary>
        /// <param name="menuElements"></param>
        /// <param name="withImage"></param>
		/// <param name="onClickHandler"></param>
        /// <returns></returns>
        internal static ToolStripItem[] CreateMenuItems(bool prependSeparator, List<FunctionMenuElement> menuElements, bool withImage, EventHandler onClickHandler)
        {
            List<ToolStripItem> items = new List<ToolStripItem>();
            string lastGroup = null;
            foreach (FunctionMenuElement menuElement in menuElements)
            {
                if (menuElement.IsForMenu)
                {
                    if (prependSeparator)
                    {
                        items.Add(new ToolStripSeparator());
                        prependSeparator = false;
                    }
                    string currGroup = menuElement.MenuGroup;
                    if (lastGroup != null && currGroup != lastGroup)
                        items.Add(new ToolStripSeparator());

                    // Položka menu této úrovně:
					bool containSubElements = menuElement.ContainSubElements;    // Pokud tato položka má submenu, pak nebude mít OnClickHandler:
					ToolStripItem toolStripItem = menuElement.GetMenuItem(withImage, (containSubElements ? null : onClickHandler));
                    // Položka má submenu?
					if (containSubElements && toolStripItem is ToolStripMenuItem)
                    {
                        ToolStripMenuItem toolStripMenuItem = toolStripItem as ToolStripMenuItem;
						toolStripMenuItem.DropDownItems.AddRange(CreateMenuItems(menuElement.SubElements, withImage, onClickHandler));
                    }
                    // Do této úrovně přidám položku (včetně submenu):
					items.Add(toolStripItem);

                    lastGroup = currGroup;
                }
            }
            return items.ToArray();
        }
        /// <summary>
        /// Vytvoří a vrátí položky toolbaru. Před první položku volitelně vloží separátor.
        /// Vytváří položky pomocí metody FunctionMenuElement.GetToolbarItem(), typicky třídy ToolStripMenuItem.
        /// </summary>
		/// <param name="prependSeparator">Před první Item předřadit separator</param>
		/// <param name="withImage">Vložit do ToolItem prvek Image</param>
		/// <param name="eventHandler">Eventhandler pro událost Click</param>
        /// <returns></returns>
		internal ToolStripItem[] CreateToolbarItems(bool prependSeparator, bool withImage, EventHandler eventHandler)
        {
			return this.CreateToolbarItems(prependSeparator, withImage, eventHandler, ToolItemType.None);
        }
        /// <summary>
        /// Vytvoří a vrátí položky toolbaru. Před první položku volitelně vloží separátor.
        /// Vytváří položky pomocí metody FunctionMenuElement.GetToolbarItem(), typicky třídy ToolStripMenuItem.
        /// </summary>
		/// <param name="prependSeparator">Před první Item předřadit separator</param>
		/// <param name="withImage">Vložit do ToolItem prvek Image</param>
		/// <param name="eventHandler">Eventhandler pro událost Click</param>
		/// <param name="toolItemType">Explicitně požadovaný typ vygenerované položky</param>
		/// <returns></returns>
		internal ToolStripItem[] CreateToolbarItems(bool prependSeparator, bool withImage, EventHandler eventHandler, ToolItemType toolItemType)
        {
			bool preSep = prependSeparator;

            List<ToolStripItem> items = new List<ToolStripItem>();

            List<FunctionMenuElement> menuElements = this.MenuElements;
            string lastGroup = null;
            foreach (FunctionMenuElement menuElement in menuElements)
            {
                if (menuElement.IsForToolbar)
                {
					if (preSep)
					{
						items.Add(new ToolStripSeparator());
						preSep = false;
					}
                    string currGroup = menuElement.MenuGroup;
                    if (lastGroup != null && currGroup != lastGroup)
                        items.Add(new ToolStripSeparator());

					items.Add(menuElement.GetToolbarItem(withImage, eventHandler, toolItemType));
                    lastGroup = currGroup;
                }
            }

            return items.ToArray();
        }
        /// <summary>
        /// Někdo kliknul na položku menu
        /// </summary>
        public void ItemClicked(ToolStripItem toolItem, FunctionContextMenuItemRunArgs args)
        {
			FunctionMenuItem functionItem = toolItem.Tag as FunctionMenuItem;
			if (functionItem == null) return;

			FunctionMenuElement.RunFunction(args);
        }
		#endregion
    }
    #endregion
    #region class FunctionMenuElement : jedna položka menu včetně vztahu na providera, včetně metod pro vygenerování vizuální položky a včetně schopností volat providera (Detect/Run)
    /// <summary>
    /// FunctionMenuElement : jedna položka menu včetně vztahu na providera, včetně metod pro vygenerování vizuální položky a včetně schopností volat providera (Detect/Run)
    /// </summary>
    public class FunctionMenuElement
    {
        #region KONSTRUKCE
        internal FunctionMenuElement(Type functionProviderType, FunctionMenuItem menuItem)
        {
            this._FunctionProviderType = functionProviderType;
            this._FunctionMenuItem = menuItem;
        }
        public override string ToString()
        {
            return "{Function=" + this.MenuText + "; Code=" + this.FunctionProviderType.FullName + "}";
        }
        private Type _FunctionProviderType;
        private FunctionMenuItem _FunctionMenuItem;
        private List<FunctionMenuElement> _SubElements;
        private bool _IsEnabledForCurrentContext;
        #endregion
        #region PROPERTY vlastní
        /// <summary> { get; }
        /// Deklarace funkce = jak ji provider funkce deklaroval. Tato deklarace se provideru zpátky předává do metody Run, pro identifikaci funkce (pokud jich jeden provider deklaruje více).
        /// </summary>
        internal FunctionMenuItem FunctionItem { get { return this._FunctionMenuItem; } }
        /// <summary> { get; }
        /// Typ funkce = Type providera, který funkci deklaruje
        /// </summary>
        internal Type FunctionProviderType { get { return this._FunctionProviderType; } }
        /// <summary>
        /// Soupis podpoložek tohoto menu, zpracované ze vstupní třídy (FunctionMenuItem) do zdejší plné třídy (FunctionMenuElement).
        /// Je to autoinicializační property, nikdy nevrací null.
        /// Pro ověření existence nějaké podpoložky je doporučeno testovat property ContainSubElements.
        /// </summary>
        internal List<FunctionMenuElement> SubElements
        { 
            get 
            {
                if (this._SubElements == null)
                    this._SubElements = new List<FunctionMenuElement>();
                return this._SubElements;
            }
        }
        /// <summary>
        /// Obsahuje true, pokud this.SubElements obsahuje nějaké položky.
        /// </summary>
        internal bool ContainSubElements { get { return (this._SubElements != null && this._SubElements.Count > 0); } }
        #endregion
        #region PROPERTY dotáhnuté z this.FunctionMenuItem
        /// <summary> { get; }
        /// Klíč položky. Slouží výhradně autorovi funkce.
        /// </summary>
        public string Key { get { return this._FunctionMenuItem.Key; } }
        /// <summary> { get; }
        /// Kompletně sestavená položka menu.
        /// Pokud funkce chce, může jí sestavit včetně všech parametrů, a takto bude položka zobrazována.
        /// </summary>
        public ToolStripItem MenuItem { get { return this._FunctionMenuItem.MenuItem; } }
        /// <summary> { get; }
        /// Kompletně sestavená položka toolbaru.
        /// Pokud funkce chce, může jí sestavit včetně všech parametrů, a takto bude položka zobrazována.
        /// </summary>
        public ToolStripItem ToolbarItem { get { return this._FunctionMenuItem.ToolbarItem; } }
        /// <summary> { get; }
        /// Typ prvku který se generuje pro Toolbar.
        /// Implicitní hodnota je None. Pro ni bude sestavena položka ToolStripMenuItem, nebo podle potřeby volajícího kódu.
        /// Pokud bude zadána explicitně, bude sestavena položka daného typu.
        /// </summary>
        public ToolItemType ToolbarType { get { return this._FunctionMenuItem.ToolbarType; } }
        /// <summary> { get; }
        /// Vlastnosti položky menu/toolbaru
        /// </summary>
        public ToolItemProperties ItemProperties { get { return this._FunctionMenuItem.ItemProperties; } }
        /// <summary> { get; }
        /// Text menu
        /// </summary>
        public string MenuText { get { return this._FunctionMenuItem.MenuText; } }
        /// <summary>
        /// Zkrácené klávesy v menu
        /// </summary>
        public Keys MenuShortcutKeys { get { return this._FunctionMenuItem.MenuShortcutKeys; } }
        /// <summary> { get; }
        /// Obrázek v menu.
        /// Pokud bude null, pak položka nebude mít v menu obrázek.
        /// Pozor: obrázek do toolbaru se definuje v this.ToolbarImage
        /// </summary>
        public Image MenuImage { get { return this._FunctionMenuItem.MenuImage; } }
        /// <summary> { get; }
        /// Obrázek v toolbaru.
        /// Pokud bude null, pak položka nebude zobrazena v toolbaru, a to ani když bude mít obrázek v this.MenuImage (tam je obrázek pouze pro menu).
        /// </summary>
        public Image ToolbarImage { get { return this._FunctionMenuItem.ToolbarImage; } }
        /// <summary> { get; }
        /// Text ToolTipu
        /// </summary>
        public string ToolTipText { get { return this._FunctionMenuItem.ToolTipText; } }
        /// <summary> { get; set; }
        /// <summary> { get; }
        /// Pokud ano, pak před zobrazením menu je volána metoda IFunctionContextMenuItem.GetMenuItemEnabledFor() s předáním informací o aktuálním elementu.
        /// </summary>
        public bool ItemEnabledByDocumentState { get { return this._FunctionMenuItem.ItemEnabledByDocumentState; } }
        /// <summary> { get; }
        /// Položky submenu (vnořené menu).
        /// Autoinstanční property (pokud dosud nebylo použito, inicializuje se na new List«FunctionMenuItem»()).
        /// Z tohoto důvodu není vhodné zjišťovat existenci submenu přes test (SubItems != null && ...), protože get zbytečně vytvoří prázdné pole.
        /// Využijte property bool ContainSubItems.
        /// </summary>
        public List<FunctionMenuItem> SubItems { get { return this._FunctionMenuItem.SubItems; } }
        /// <summary>
        /// Příznak, že tato položka menu obsahuje submenu
        /// </summary>
        public bool ContainSubItems { get { return this._FunctionMenuItem.ContainSubItems; } }
        /// <summary> { get; }
        /// Skupina menu. Pokud chceme mít svoje položky menu pohromadě, pak sem vložíme unikátní název skupiny. 
        /// Hodnotou MenuOrder zajistíme třídění položek v rámci skupiny.
        /// </summary>
        public string MenuGroup { get { return (!String.IsNullOrEmpty(this._FunctionMenuItem.MenuGroup) ? this._FunctionMenuItem.MenuGroup: String.Empty); } }
        /// <summary> { get; }
        /// Pořadí položky menu v rámci její skupiny MenuGroup.
        /// </summary>
        public int MenuOrder { get { return this._FunctionMenuItem.MenuOrder; } }
        /// <summary> { get; set; }
        /// Prostor pro uložení tagu. Slouží výhradně autorovi funkce.
        /// </summary>
        public object Tag { get { return this._FunctionMenuItem.Tag; } set { this._FunctionMenuItem.Tag = value; } }
        /// <summary> { get; }
        /// Položka je pro aktuální situaci enabled?
        /// </summary>
        public bool IsEnabledForCurrentContext { get { return this._IsEnabledForCurrentContext; } }
        /// <summary> { get; }
        /// Příznak, že MenuText není empty a položka se má dostat do menu
        /// </summary>
        public bool IsForMenu { get { return this._FunctionMenuItem.IsForMenu; } }
        /// <summary> { get; }
        /// Příznak, že ToolbarImage není null a položka se má dostat do toolbaru
        /// </summary>
        public bool IsForToolbar { get { return this._FunctionMenuItem.IsForToolbar; } }
        #endregion
        #region COMPARER
        /// <summary>
        /// Porovná dvě položky menu podle pořadí, v němž mají být zobrazeny.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        internal static int CompareByOrder(FunctionMenuElement a, FunctionMenuElement b)
        {
            int cmp = String.Compare(a.MenuGroup, b.MenuGroup);
            if (cmp != 0) return cmp;
            cmp = a.MenuOrder.CompareTo(b.MenuOrder);
            if (cmp != 0) return cmp;
            cmp = a.MenuText.CompareTo(b.MenuText);
            if (cmp != 0) return cmp;
            return String.Compare(a.FunctionProviderType.FullName, b.FunctionProviderType.FullName);
        }
        #endregion
        #region GENERÁTOR POLOŽKY MENU a TOOLBARU, SPOUŠTĚČ AKCE Z POLOŽKY MENU
        /// <summary>
        /// Položka menu si zjistí pro konkrétní situaci, zda pro danou situaci má být aktivní.
        /// Výsledek si ukládá do this.EnabledForContextState
        /// </summary>
        /// <param name="runArgs"></param>
        internal void SetEnabledForContextState(FunctionContextMenuItemRunArgs runArgs)
        {
            this._IsEnabledForCurrentContext = false;
            if (this.ItemEnabledByDocumentState)
            {	// Stav menu je závislý na aktuálním stavu editoru => stav editoru je popsán v parametru:
                try
                {
                    IFunctionContextMenuItem functionObject = Steward.GetServiceObject<IFunctionContextMenuItem>(this.FunctionProviderType, true);
                    if (functionObject != null)
                    {
                        runArgs.FunctionItem = this.FunctionItem;
                        this._IsEnabledForCurrentContext = functionObject.IsMenuItemEnabledFor(runArgs);
                    }
                }
                catch (Exception)
                {
                    this._IsEnabledForCurrentContext = false;
                }
            }
            else
            {
                this._IsEnabledForCurrentContext = true;
            }
        }
		#region Generování prvků do Menu
		/// <summary>
        /// Vygeneruje a vrátí položku menu.
        /// Data má uložená v sobě.
        /// Do Tag je uložen this = FunctionMenuElement.
        /// Neřeší tvorbu SubMenu ze SubElements.
        /// </summary>
        /// <returns></returns>
		internal ToolStripItem GetMenuItem(bool withImage, EventHandler onClickHandler)
        {
            ToolStripItem item;
            if (this.MenuItem != null)
            {
                item = this.MenuItem;
			}
            else
            {
				if (this.MenuText == "-")
				{
					return new ToolStripSeparator();
				}
				else
				{
					item = new ToolStripMenuItem();
					item.Name = this.MenuText;
					item.Text = this.MenuText;
					item.ToolTipText = this.ToolTipText;
					if (withImage)
						item.Image = this.MenuImage;
				}
            }

            item.Tag = _GetFunctionItemWithProvider(this._FunctionMenuItem, this.FunctionProviderType);
            if (onClickHandler != null)
                item.Click += onClickHandler;

            item.Enabled = (this.ItemEnabledByDocumentState ? this._IsEnabledForCurrentContext : true);
            // item.Tag = this;
            return item;
        }
		#endregion
		#region Generování prvků do Toolbaru
        /// <summary>
        /// Vygeneruje a vrátí položku toolbaru.
        /// Data má uložená v sobě.
        /// Do Tag je uložen this = FunctionMenuElement.
        /// </summary>
        /// <returns></returns>
        internal ToolStripItem GetToolbarItem(bool withImage, EventHandler clickEventHandler)
        {
			return GetToolbarItem(withImage, clickEventHandler, ToolItemType.ToolStripMenuItem);
        }
        /// <summary>
        /// Vygeneruje a vrátí položku toolbaru.
        /// Data má uložená v sobě.
        /// Do Tag je uložen this = FunctionMenuElement.
        /// </summary>
        /// <returns></returns>
		internal ToolStripItem GetToolbarItem(bool withImage, EventHandler clickEventHandler, ToolItemType toolItemType)
        {
            ToolStripItem item = null;
            if (this.ToolbarItem != null)
            {
                item = this.ToolbarItem;
				item.Tag = _GetFunctionItemWithProvider(this._FunctionMenuItem, this.FunctionProviderType);
				if (clickEventHandler != null)
					item.Click += clickEventHandler;
            }
            else
            {
				item = _GetToolbarItem(this._FunctionMenuItem, this.FunctionProviderType, withImage, clickEventHandler, toolItemType);
				_AddToolbarSubItems(this._FunctionMenuItem, this.FunctionProviderType, item, withImage, clickEventHandler, toolItemType);
            }
            item.Enabled = (this.ItemEnabledByDocumentState ? this._IsEnabledForCurrentContext : true);
            // item.Tag = this;
            return item;
        }
		/// <summary>
		/// Vytvoří položku Toolbaru
		/// </summary>
		/// <param name="functionItem"></param>
		/// <param name="withImage"></param>
		/// <param name="clickEventHandler"></param>
		/// <param name="toolItemType"></param>
		/// <returns></returns>
		private static ToolStripItem _GetToolbarItem(FunctionMenuItem functionItem, Type functionProviderType, bool withImage, EventHandler clickEventHandler, ToolItemType toolItemType)
		{
			ToolStripItem item = null;

			// Druh položky: program má přednost - když program žádá o například SplitButton, dostane jej. Pokud je to programu jedno (None), dostane to co chce item:
			ToolItemType itemType = (toolItemType != ToolItemType.None ? toolItemType : functionItem.ToolbarType);
			switch (itemType)
			{
				case ToolItemType.None:
				case ToolItemType.ToolStripButton:
					ToolStripButton buttonItem = new ToolStripButton();
					buttonItem.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
					buttonItem.Name = functionItem.MenuText;
					buttonItem.Text = functionItem.MenuText;
					if (withImage)
						buttonItem.Image = functionItem.ToolbarImage;
					buttonItem.ToolTipText = functionItem.ToolTipText;
					buttonItem.Size = (functionItem.ToolbarImage == null ? new Size(16, 16) : functionItem.ToolbarImage.Size);
					if (clickEventHandler != null)
						buttonItem.Click += clickEventHandler;
					item = buttonItem;
					break;
				case ToolItemType.ToolStripSplitButton:
					ToolStripSplitButton splitItem = new ToolStripSplitButton();
					splitItem.Name = functionItem.MenuText;
					splitItem.Text = functionItem.MenuText;
					if (withImage)
						splitItem.Image = functionItem.ToolbarImage;
					splitItem.ToolTipText = functionItem.ToolTipText;
					if (clickEventHandler != null)
						splitItem.ButtonClick += clickEventHandler;
					item = splitItem;
					break;
				case ToolItemType.ToolStripDropDownButton:
					ToolStripDropDownButton ddButton = new ToolStripDropDownButton();
					ddButton.Name = functionItem.MenuText;
					ddButton.Text = functionItem.MenuText;
					if (withImage)
						ddButton.Image = functionItem.ToolbarImage;
					ddButton.ToolTipText = functionItem.ToolTipText;
					if (clickEventHandler != null)
						ddButton.Click += clickEventHandler;
					item = ddButton;
					break;

				case ToolItemType.ToolStripMenuItem:
				default:
					ToolStripMenuItem menuItem = new ToolStripMenuItem();
					menuItem.Name = functionItem.MenuText;
					menuItem.Text = functionItem.MenuText;
					if (withImage)
						menuItem.Image = functionItem.ToolbarImage;
					menuItem.ToolTipText = functionItem.ToolTipText;
					if (clickEventHandler != null)
						menuItem.Click += clickEventHandler;
					item = menuItem;
					break;
			}
			item.ImageScaling = ToolStripItemImageScaling.None;
			item.Tag = _GetFunctionItemWithProvider(functionItem, functionProviderType);

			return item;
		}
		/// <summary>
		/// Do připravené položky Toolbaru vloží SubItems
		/// </summary>
		/// <param name="functionItem"></param>
		/// <param name="item"></param>
		/// <param name="withImage"></param>
		/// <param name="clickEventHandler"></param>
		/// <param name="toolItemType"></param>
		private static void _AddToolbarSubItems(FunctionMenuItem functionItem, Type functionProviderType, ToolStripItem item, bool withImage, EventHandler clickEventHandler, ToolItemType toolItemType)
		{
			if (functionItem == null || functionItem.SubItems == null)
				return;

			if (item is ToolStripMenuItem)
			{
				ToolStripMenuItem menuItem = item as ToolStripMenuItem;
				foreach (FunctionMenuItem subItem in functionItem.SubItems)
					menuItem.DropDownItems.Add(_GetToolbarItem(subItem, functionProviderType, withImage, clickEventHandler, ToolItemType.ToolStripMenuItem));
			}
			else if (item is ToolStripDropDownButton)
			{
				ToolStripDropDownButton ddButton = item as ToolStripDropDownButton;
				foreach (FunctionMenuItem subItem in functionItem.SubItems)
					ddButton.DropDownItems.Add(_GetToolbarItem(subItem, functionProviderType, withImage, clickEventHandler, ToolItemType.ToolStripMenuItem /* toolItemType */));
			}
		}
		private static FunctionMenuItem _GetFunctionItemWithProvider(FunctionMenuItem functionItem, Type functionProviderType)
		{
			functionItem.FunctionProviderType = functionProviderType;
			return functionItem;
		}
		#endregion
		/// <summary>
        /// Spustí funkci, která je navázaná na dané položce menu.
        /// Položka menu je vytvořena metodou GetToolStripItem(), a v jejím Tagu je uložen FunctionMenuElement anebo Type cílové funkce.
        /// Zdejší metoda je static, protože není k dispozici reference na vlastní položku ServiceMenuOneItem, která odpovídá této funkci,
        /// a zatím ani tuto položku nepotřebujeme. Stačí Type funkce.
        /// </summary>
		/// <param name="args">Data pro spuštění funkce = obsahují informace o grafu, řádku a elementu odkud se funkce volá</param>
		internal static void RunFunction(FunctionGlobalRunArgs args)
        {
            // Získat Type providera funkce:
			Type functionType = args.MenuItem.FunctionProviderType;
            if (functionType == null) return;

            // Získat providera funkce:
            IFunctionGlobalProvider globalProvider = Steward.ObjectGenerator<IFunctionGlobalProvider>(functionType, true);
            if (globalProvider == null) return;

            try
            {
                // Tady by to chtělo spustit nový Thread, v něm spustit funkci, a current thread dát uspat do konce běhu funkce,
                // aby vizuální vrstva nebyla mrtvá, ale živá a čekající.
                globalProvider.Run(args);
            }
            catch (Exception exc)
            {	// Odchytím i System výjimky, a zobrazím je.
                // To proto, že volaná funkce může chybovat jakkoli, ale nemá to shodit celé okno.
                Msg.Error("Při běhu funkce došlo k chybě:" + exc.Message);
            }
        }
        /// <summary>
        /// Spustí funkci, která je navázaná na dané položce menu.
        /// Položka menu je vytvořena metodou GetToolStripItem(), a v jejím Tagu je uložen FunctionMenuElement anebo Type cílové funkce.
        /// Zdejší metoda je static, protože není k dispozici reference na vlastní položku ServiceMenuOneItem, která odpovídá této funkci,
        /// a zatím ani tuto položku nepotřebujeme. Stačí Type funkce.
        /// </summary>
		/// <param name="function">Deklarace funkce</param>
		/// <param name="args">Data pro spuštění funkce = obsahují informace o grafu, řádku a elementu odkud se funkce volá</param>
		internal static void RunFunction(FunctionContextMenuItemRunArgs args)
        {
            // Získat Type providera funkce:
			Type functionType = args.FunctionItem.FunctionProviderType;
            if (functionType == null) return;

            IFunctionContextMenuItem contextProvider = Steward.ObjectGenerator<IFunctionContextMenuItem>(functionType, true);
            if (contextProvider == null) return;

            try
            {
                // Tady by to chtělo spustit nový Thread, v něm spustit funkci, a current thread dát uspat do konce běhu funkce,
                // aby vizuální vrstva nebyla mrtvá, ale živá a čekající.
                contextProvider.Run(args);
            }
            catch (Exception exc)
            {	// Odchytím i System výjimky, a zobrazím je.
                // To proto, že volaná funkce může chybovat jakkoli, ale nemá to shodit celé okno.
                Msg.Error("Při běhu funkce došlo k chybě:" + exc.Message);
                System.Windows.Forms.Clipboard.SetText(exc.Message + exc.StackTrace);
            }
        }
        #endregion
    }
    #endregion
}
